package cn.xzzz2020.XJynWeb.ioc.inject;

import cn.xzzz2020.XJynWeb.ioc.core.BeanContainer;
import cn.xzzz2020.XJynWeb.ioc.inject.annotation.Autowired;
import cn.xzzz2020.XJynWeb.utils.ClassUtil;
import cn.xzzz2020.XJynWeb.utils.ValidationUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * 提供依赖注入
 *
 * @author xzzz2020
 * @version 1.0
 * @date 2020/7/31 17:12
 */
@Slf4j
public class DependencyInjector {
    //Bean容器
    private BeanContainer beanContainer;

    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行依赖注入
     */
    public void doIoc() {
        if (ValidationUtil.isEmpty(beanContainer.getClasses())) {
            log.warn("empty classset in BeanContainer");
            return;
        }
        //1.遍历Bean容器中所有的Class对象
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> clazz : classSet) {
            //2.遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)){
                continue;
            }
            //3.找出被Autowired标记的成员变量
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)){
                    //获取属性上面的注解，来解析属性
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String value = autowired.value();
                    //4.获取这些成员变量的类型
                    Class<?> fieldClazz = field.getType();
                    //5.获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = null;
                    try {
                        fieldValue = getFieldInstance(fieldClazz,value);
                    } catch (Exception e) {
                        throw new RuntimeException(e.getMessage()+" in " +clazz.getName());
                    }
                    //获取不到对象，注入失败
                    if (fieldValue == null){
                        throw new RuntimeException("unable to inject relevant type，target fieldClass is:" + fieldClazz.getName() + " in " +clazz.getSimpleName() );
                    }
                    //6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
                    Object targetBean = beanContainer.getBean(clazz);
                    ClassUtil.setField(field,targetBean,fieldValue,true);
                }
            }

        }

    }

    /**
     * 根据class对象获取其对象或者实现类
     * @param fieldClazz 属性定义的对象
     * @return
     */
    private Object getFieldInstance(Class<?> fieldClazz,String value) throws Exception{
        Object fieldValue = beanContainer.getBean(fieldClazz);
        if (fieldValue!=null){
            return fieldClazz;
        }else {
            Class<?> implClass = null;
            try {
                implClass = getImplementClass(fieldClazz,value);
            } catch (Exception e) {
                throw new Exception(e);
            }
            if (implClass!=null){
               return beanContainer.getBean(implClass);
           }else {
               return null;
           }
        }
    }

    /**
     * 获取接口的实现类
     * @param fieldClazz 接口
     * @return
     */
    private Class<?> getImplementClass(Class<?> fieldClazz,String value) throws Exception{
        Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldClazz);
        if (!ValidationUtil.isEmpty(classSet)){
            if (ValidationUtil.isEmpty(value)){//代表用户没有指定具体是那个实现类
                if (classSet.size()==1){
                    return classSet.iterator().next();
                }else {//如果该接口有多个实现类，则抛出异常
                    throw new Exception("multiple implemented classes for " + fieldClazz.getName() + " please set @Autowired's value to pick one");
                }
            }else {//用户指定具体实现子类
                for (Class<?> clazz : classSet) {
                    if (value.equals(clazz.getSimpleName())){//是用户的指定类
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
}
